package com.xhtt.modules.inspection.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xhtt.common.exception.RRException;
import com.xhtt.common.exception.RRExceptionEnum;
import com.xhtt.common.utils.*;
import com.xhtt.modules.inspection.dao.InsTaskMapper;
import com.xhtt.modules.inspection.dto.InsTaskDTO;
import com.xhtt.modules.inspection.entity.*;
import com.xhtt.modules.inspection.enums.*;
import com.xhtt.modules.inspection.schedule.InsMsgEntity;
import com.xhtt.modules.inspection.service.*;
import com.xhtt.modules.inspection.util.InsTaskUtil;
import com.xhtt.modules.inspection.vo.InsLineVO;
import com.xhtt.modules.inspection.vo.InsTaskVO;
import com.xhtt.modules.inspection.vo.InspectionAppVO;
import com.xhtt.modules.msg.entity.MsgInfo;
import com.xhtt.modules.msg.service.MsgUserService;
import com.xhtt.modules.sys.entity.SysDeptEntity;
import com.xhtt.modules.sys.entity.SysUserDeptEntity;
import com.xhtt.modules.sys.entity.SysUserEntity;
import com.xhtt.modules.sys.service.SysUserDeptService;
import com.xhtt.modules.sys.service.SysUserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 巡检任务 服务实现类
 * </p>
 *
 * @author xj
 * @since 2019-01-18
 */
@Service
@Slf4j
public class InsTaskServiceImpl extends ServiceImpl<InsTaskMapper, InsTask> implements IInsTaskService {

    @Autowired
    private IInsTaskDateService insTaskDateService;

    @Autowired
    private IInsTaskUserService insTaskUserService;

    @Autowired
    private IInsLineService insLineService;

    @Autowired
    private IInsClassesTeamDetailService insClassesTeamDetailService;

    @Autowired
    private IInsTaskStepService insTaskStepService;

    @Autowired
    private IInsTaskStepPointService insTaskStepPointService;

    @Autowired
    private IInsLinePointService insLinePointService;

    @Autowired
    private MsgUserService msgUserService;

    @Autowired
    private SysUserService sysUserService;

    @Autowired
    private SysUserDeptService sysUserDeptService;

    @Autowired
    private IInsPointService insPointService;

    @Resource
    private ThreadPoolTaskExecutor taskExecutor;

    @Resource
    private TransactionTemplate transactionTemplate;

    // 一星期天数
    private static final int dayNum = 7;

    private static final String datetimePattern = DateUtils.DATE_TIME_PATTERN;

    private static final String datePattern = DateUtils.DATE_PATTERN;

    private static final DateTimeFormatter df = DateTimeFormatter.ofPattern(datetimePattern);

    @Override
    public PageUtils queryPage(Map<String, Object> params) {

        Page<InsTask> page = new Query<InsTask>(params).getPage();

        // 根据线路名称模糊查询获取线路id
        List<InsLine> insLineList = insLineService.getListByName((String) params.get("lineName"));
        List<Integer> lineIds = insLineList.stream().map(InsLine::getId).collect(Collectors.toList());

        IPage<InsTask> iPage = baseMapper
                .selectPage(page, new LambdaQueryWrapper<InsTask>()
                        .like(Optional.ofNullable(params.get("taskName")).isPresent(), InsTask::getTaskName, params.get("taskName"))
                        .eq(Optional.ofNullable(params.get("taskType")).isPresent(), InsTask::getTaskType, params.get("taskType"))
                        .in(CollectionUtils.isNotEmpty(lineIds), InsTask::getLineId, lineIds)
                        .eq(Optional.ofNullable(params.get("inspectType")).isPresent(), InsTask::getInspectType, params.get("inspectType"))
                        .eq(InsTask::getTeamId, params.get("teamId"))
                        .orderByDesc(InsTask::getUpdateTime));

        List<InsTask> taskList = iPage.getRecords();

        if (CollectionUtils.isNotEmpty(taskList)) {
            for (InsTask e : taskList) {
                // 任务id
                Integer taskId = e.getId();

                // 线路id
                Integer lineId = e.getLineId();
                // 设置线路名称
                InsLine insLine = insLineService.getAllById(lineId);
                if (lineId.equals(insLine.getId())) {
                    e.setLineName(insLine.getName());
                }

                // 获取班组名称列表
                Integer userId = e.getCreateUserId();

                SysDeptEntity sysDeptEntity = sysUserDeptService.getDeptIsGroupByUserId(userId);
                if (sysDeptEntity != null) {
                    String groupName = sysDeptEntity.getName();
                    e.setGroupName(groupName);
                }
                List<InsTaskUser> insTaskUsers = insTaskUserService
                        .list(new LambdaQueryWrapper<InsTaskUser>()
                                .eq(InsTaskUser::getTaskId, taskId)
                                .eq(InsTaskUser::getIsUpdate, 3));
                if (CollectionUtils.isNotEmpty(insTaskUsers)) {

                    try {
                        String insUser = insTaskUsers.stream().map(taskUser -> sysUserService.getById(taskUser.getUserId()).getName()).collect(Collectors.joining(","));
                        e.setInsUser(insUser);
                    } catch (Exception ex) {
                        ex.printStackTrace();
                        log.error("设置巡检人员时出错 任务所涉及的人员对象={}", JsonUtil.toJson(insTaskUsers));
                    }
                }
            }
        }
        return new PageUtils(iPage);
    }


    @Override
    @Transactional
    public void add(InsTaskVO insTaskVO) {

        // 保存任务表
        InsTask insTask = new InsTask();

        InsTaskDTO insTaskDTO = InsTaskUtil.insTaskVO2InsTaskDTO(insTaskVO);

        BeanUtils.copyProperties(insTaskDTO, insTask);

        // 任务结束时间
        LocalDateTime taskEndTime = DateUtils.string2LocalDateTime(insTaskVO.getTaskTime().get(1), datePattern);

        // 任务开始时间
        LocalDateTime taskStartTime = DateUtils.string2LocalDateTime(insTaskVO.getTaskTime().get(0), datePattern);

        insTask.setTaskStartDate(taskStartTime);
        insTask.setTaskEndDate(taskEndTime);

        baseMapper.insert(insTask);

        addUser(insTaskVO, insTask.getId());

        addItem(insTaskVO, insTask);
    }


    @Override
    @Transactional
    public void update(InsTaskVO insTaskVO) {

        // 修改任务表
        InsTask insTask = new InsTask();
        InsTaskDTO insTaskDTO = InsTaskUtil.insTaskVO2InsTaskDTO(insTaskVO);

        BeanUtils.copyProperties(insTaskDTO, insTask);

        // 任务开始时间
        LocalDateTime taskStartTime = DateUtils.string2LocalDateTime(insTaskVO.getTaskTime().get(0), datePattern);

        // 任务结束时间
        LocalDateTime taskEndTime = DateUtils.string2LocalDateTime(insTaskVO.getTaskTime().get(1), datePattern);

        // 任务主表id
        Integer taskId = insTaskDTO.getTaskId();

        insTask.setId(taskId);

        insTask.setTaskStartDate(taskStartTime);
        insTask.setTaskEndDate(taskEndTime);

        baseMapper.updateById(insTask);

        deleteItem(new ArrayList<Integer>() {{
            add(taskId);
        }}, false);

        // 如果修改的开始时间大于今天,则不会删除今天以及之前的的记录
        insTaskDateService.remove(new LambdaQueryWrapper<InsTaskDate>()
                .in(InsTaskDate::getTaskId, taskId)
                .gt(taskStartTime.isAfter(DateUtils.now()), InsTaskDate::getDateTime, DateUtils.now()));

        addUser(insTaskVO, insTask.getId());

        addItem(insTaskVO, insTask);

    }

    @Override
    @Transactional
    public void delete(List<Integer> ids) {

        deleteItem(ids, true);
        // 删除任务人员表
//        insTaskUserService.remove(new LambdaQueryWrapper<InsTaskUser>().in(InsTaskUser::getTaskId, ids).ne(InsTaskUser::getIsUpdate, 3));

        // 任务表
        ids.forEach(id -> {
            InsTask insTask = this.getById(id);
            if (insTask != null) {
                insTask.setDeleteTime(DateUtils.now());
                this.updateById(insTask);
            }
        });

        baseMapper.deleteBatchIds(ids);

    }

    /**
     * 添加巡检人员，添加任务用的是异步，会导致刷新页面时获取不到最新数据，
     * 所以添加任务时候先保存一下人员。
     */
    private void addUser(InsTaskVO insTaskVO, Integer taskId) {

        // 人员列表
        List<Integer> users = insTaskVO.getUserTeam();

        // 创建任务和人员关联数据,当所有人员全部重新排班后获取这条信息
        List<InsTaskUser> list = new ArrayList<>();
        users.forEach(e -> {
            InsTaskUser insTaskUser = new InsTaskUser();
            insTaskUser.setTaskId(taskId);
            insTaskUser.setSort(0);
            insTaskUser.setUserId(e);
            insTaskUser.setIsUpdate(3);
            list.add(insTaskUser);
        });
        insTaskUserService.saveBatch(list);
    }

    private void addItem(InsTaskVO insTaskVO, InsTask insTask) {
        taskExecutor.submit(() -> {
            transactionTemplate.execute(transactionStatus -> {
                try {
                    // 获取任务时间段的所包含的天的集合
                    List<LocalDateTime> taskDays = DateUtils
                            .getDays(insTaskVO.getTaskTime().get(0), insTaskVO.getTaskTime().get(1), datePattern);

                    // 人员列表
                    List<Integer> users = insTaskVO.getUserTeam();

                    // 保存任务的子表-时间表  一天对应一条记录
                    insTaskDateService.addDateAndUser(taskDays, insTask, users);

                    // 添加明细
                    addTaskStep(taskDays, insTask, users);
                } catch (Throwable e) {
                    e.printStackTrace();
                    transactionStatus.setRollbackOnly();
                } finally {
                    return true;
                }
            });
        });
    }

    /**
     * 添加任务明细表-计算后的任务
     *
     * @param taskDays 开始-结束时间集合
     * @param insTask  任务对象
     */
    private void addTaskStep(List<LocalDateTime> taskDays, InsTask insTask, List<Integer> users) {

        // 巡检类型
        Integer inspectType = insTask.getInspectType();
        List<InsTaskStep> insTaskSteps = new ArrayList<>();
        // 周巡检
        if (inspectType == 2) {

            insTaskSteps = insTaskStepService.addWeekOrMonth(groupByWeek(taskDays), insTask);
            // 月巡检
        } else if (inspectType == 3) {

            insTaskSteps = insTaskStepService.addWeekOrMonth(groupByMonth(taskDays), insTask);
            // 日巡检
        } else if (inspectType == 1) {
            insTaskSteps = new ArrayList<>();
            for (LocalDateTime taskDay : taskDays) {
                /*
                 * 去排班表查询
                 * 1.先判断今天是否有排班(日期比较)
                 * 2.判断今天有没有排发布人这个组的班
                 * 3.判断这个组是上日班还是夜班
                 * 4.决定选择日班时间段还是夜班时间段  (没实现：暂时不做判断,日班夜班都添加,重新排班也好取)
                 */
                InsClassesTeamDetail insClassesTeamDetail = insClassesTeamDetailService
                        .getOne(new LambdaQueryWrapper<InsClassesTeamDetail>()
                                .eq(InsClassesTeamDetail::getDate, taskDay));

                if (insClassesTeamDetail == null) {
                    log.info(taskDay + RRExceptionEnum.NOT_SCHEDULING.getMsg());
                } else {

                    // 日班
                    if (insClassesTeamDetail.getTeamMorning().equals(insTask.getTeamId())) {
                        // 日班时间段
                        List<List<LocalDateTime>> morningStepResults = groupByDay(
                                insTask.getInspectMorningStartDate().withYear(taskDay.getYear()).withMonth(taskDay.getMonthValue()).withDayOfMonth(taskDay.getDayOfMonth()),
                                insTask.getInspectMorningEndDate().withYear(taskDay.getYear()).withMonth(taskDay.getMonthValue()).withDayOfMonth(taskDay.getDayOfMonth()),
                                insTask.getInspectRate());
                        List<InsTaskStep> insTaskStepMorns = insTaskStepService.addDay(morningStepResults, insTask, ShiftTypeEnum.DAY.getCode());
                        if (CollectionUtils.isNotEmpty(insTaskStepMorns)) {
                            insTaskSteps.addAll(insTaskStepMorns);
                        }

                    }
                    // 夜班
                    if (insClassesTeamDetail.getTeamNight().equals(insTask.getTeamId())) {

                        // 夜班时间段
                        List<List<LocalDateTime>> nightStepResults = groupByDay(
                                insTask.getInspectNightStartDate().withYear(taskDay.getYear()).withMonth(taskDay.getMonthValue()).withDayOfMonth(taskDay.getDayOfMonth()),
                                insTask.getInspectNightEndDate().withYear(taskDay.getYear()).withMonth(taskDay.getMonthValue()).withDayOfMonth(taskDay.getDayOfMonth()),
                                insTask.getInspectRate());
                        List<InsTaskStep> insTaskStepNights = insTaskStepService.addDay(nightStepResults, insTask, ShiftTypeEnum.NIGHT.getCode());
                        if (CollectionUtils.isNotEmpty(insTaskStepNights)) {
                            insTaskSteps.addAll(insTaskStepNights);
                        }
                    }
                }
            }
        }

        // 添加子任务与人员关联
        List<InsTaskUser> insTaskUsers = new ArrayList<>();
        for (InsTaskStep insTaskStep : insTaskSteps) {
            Integer taskStepId = insTaskStep.getId();
            InsTaskUser insTaskUser;
            for (Integer userId : users) {
                insTaskUser = new InsTaskUser();
                insTaskUser.setUserId(userId);
                insTaskUser.setTaskId(0);
                insTaskUser.setTaskDateId(0);
                insTaskUser.setTaskStepId(taskStepId);
                insTaskUsers.add(insTaskUser);
            }
        }
        insTaskUserService.saveBatch(insTaskUsers);
    }

    private void deleteItem(List<Integer> ids, boolean del) {
        // 任务时间表, 今天和今天之前的的日期不删除, 任务删除后在排班还是可以查询
        if (del) {
            insTaskDateService.remove(new LambdaQueryWrapper<InsTaskDate>().in(InsTaskDate::getTaskId, ids).gt(InsTaskDate::getDateTime, DateUtils.now()));
        }


        // 任务人员表
        insTaskUserService.remove(new LambdaQueryWrapper<InsTaskUser>().in(InsTaskUser::getTaskId, ids).ne(del, InsTaskUser::getIsUpdate, 3));

        // 查询记录表，如果存在这个子任务的记录就不删除
        // 这边其实是多余的，下面查询已经有今天和今天之前的日期不需要删除的条件。
        List<InsTaskStepPoint> insTaskStepPoints = insTaskStepPointService.list();
        List<Integer> insTaskStepId = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(insTaskStepPoints)) {
            insTaskStepId = insTaskStepPoints.stream().map(InsTaskStepPoint::getTaskStepId).distinct().collect(Collectors.toList());
        }

        List<Integer> finalInsTaskStepId = insTaskStepId;
        ids.forEach(taskId -> {
            InsTask instask = baseMapper.selectById(taskId);
            if (instask != null) {
                Integer inspectType = instask.getInspectType();
                // 日巡检不需要判断是否推送，当日的不删除
                if (inspectType.equals(InspectTypeEnum.DAY.getCode())) {

                    // 根据taskId 查出当天的任务step
                    InsTaskStep insTaskStep = insTaskStepService
                            .getOne(new LambdaQueryWrapper<InsTaskStep>()
                                    .eq(InsTaskStep::getTaskId, taskId)
                                    .eq(InsTaskStep::getTaskStartDate, DateUtils.now())
                                    .last("limit 1"));

//                    List<InsTaskStep> insTaskSteps = insTaskStepService
//                            .list(new LambdaQueryWrapper<InsTaskStep>()
//                                    .eq(InsTaskStep::getTaskId, taskId)
//                                    .eq(InsTaskStep::getTaskStartDate, DateUtils.now)
//                                    .orderByAsc(InsTaskStep::getId));
//                    List<String> steps = new ArrayList<>();

                    LambdaQueryWrapper<InsTaskStep> lambdaQueryWrapper;


                    if (insTaskStep != null) {
//                        steps = insTaskSteps.stream().map(InsTaskStep::getStep).distinct().collect(Collectors.toList());

                        lambdaQueryWrapper = new LambdaQueryWrapper<InsTaskStep>()
                                .eq(InsTaskStep::getTaskId, taskId)
                                .ne(InsTaskStep::getStep, insTaskStep.getStep())
                                .notIn(InsTaskStep::getId, finalInsTaskStepId)
                                .gt(InsTaskStep::getTaskStartDate, DateUtils.now());
                    } else {
                        // 如果今天的子任务为空,也就不存在跨天
                        lambdaQueryWrapper = new LambdaQueryWrapper<InsTaskStep>()
                                .eq(InsTaskStep::getTaskId, taskId)
                                .notIn(InsTaskStep::getId, finalInsTaskStepId)
                                .gt(InsTaskStep::getTaskStartDate, DateUtils.now());

                    }
                    // 先查询出来, 获取所有即将删除的stepId 然后去删除任务人员表里面的数据
                    List<InsTaskStep> insTaskStepsList = insTaskStepService.list(lambdaQueryWrapper);
                    List<Integer> taskStepIds = insTaskStepsList.stream().map(InsTaskStep::getId).distinct().collect(Collectors.toList());

                    if (CollectionUtils.isNotEmpty(insTaskStepsList)) {
                        // 删除人员
                        insTaskUserService.remove(new LambdaQueryWrapper<InsTaskUser>().in(InsTaskUser::getTaskStepId, taskStepIds));
                        // 任务明细表
                        insTaskStepService.remove(lambdaQueryWrapper);
                    }
                } else {
                    // 周巡检和月巡检只需要判断是否推送即可
                    insTaskStepService.remove(new LambdaQueryWrapper<InsTaskStep>()
                            .in(InsTaskStep::getTaskId, ids)
                            .ne(InsTaskStep::getPushed, InsPushEnum.Y.getCode())
                            .notIn(InsTaskStep::getId, finalInsTaskStepId));
                }
            }

        });
    }

    @Override
    public InsTaskVO detail(Integer taskId) {

        // 构造返回对象
        InsTaskVO insTaskDTO = new InsTaskVO();

        InsTask insTask = baseMapper.selectById(taskId);

        Integer taskTeamId = insTask.getTeamId();

        BeanUtils.copyProperties(insTask, insTaskDTO);

        // 获取线路
        InsLine insLine = insLineService.getAllById(insTask.getLineId());

        // 线路名称
        insTaskDTO.setLineName(insLine.getName());

        // 添加任务时间
        insTaskDTO.setTaskTime(Arrays.asList(df.format(insTask.getTaskStartDate()), df.format(insTask.getTaskEndDate())));

        List<LocalDateTime> taskTimeList = DateUtils.getDays(df.format(insTask.getTaskStartDate()), df.format(insTask.getTaskEndDate()), DateUtils.DATE_PATTERN);

        // 这个班组排班情况和任务时间取交集获得可以调整的日期
        List<LocalDateTime> classesDateList = insClassesTeamDetailService.getDatesByTeamId(taskTeamId);

        classesDateList.retainAll(taskTimeList);

        insTaskDTO.setTaskTimeList(classesDateList);

        // 添加巡检时间
        if (insTask.getInspectMorningStartDate() != null) {
            insTaskDTO.setInspectMorningStartDate(df.format(insTask.getInspectMorningStartDate()));
        }
        if (insTask.getInspectMorningEndDate() != null) {
            insTaskDTO.setInspectMorningEndDate(df.format(insTask.getInspectMorningEndDate()));
        }
        if (insTask.getInspectNightStartDate() != null) {
            insTaskDTO.setInspectNightStartDate(df.format(insTask.getInspectNightStartDate()));
        }
        if (insTask.getInspectNightEndDate() != null) {
            insTaskDTO.setInspectNightEndDate(df.format(insTask.getInspectNightEndDate()));
        }

        // 人员id
        List<InsTaskUser> insTaskUsers = insTaskUserService
                .list(new LambdaQueryWrapper<InsTaskUser>()
                        .eq(InsTaskUser::getIsUpdate, 1)
                        .in(InsTaskUser::getTaskId, taskId));
        if (CollectionUtils.isEmpty(insTaskUsers)) {
            insTaskUsers = insTaskUserService
                    .list(new LambdaQueryWrapper<InsTaskUser>()
                            .eq(InsTaskUser::getIsUpdate, 3)
                            .in(InsTaskUser::getTaskId, taskId));
        }
        Set<Integer> userIds = insTaskUsers.stream().map(InsTaskUser::getUserId).collect(Collectors.toSet());

        insTaskDTO.setUserTeam(new ArrayList<>(userIds));

        return insTaskDTO;
    }

    @Override
    public InsTaskVO queryDetailByDate(Map<String, Object> params) {

        // 构造返回对象
        InsTaskVO insTaskDTO = new InsTaskVO();

        Integer taskId = Integer.parseInt(params.get("id").toString());

        InsTask insTask = baseMapper.selectById(taskId);

        Integer teamId = insTask.getTeamId();

        String date = params.get("date").toString();

        // 获取排班情况
        int shiftType = insClassesTeamDetailService.getShiftTypeByDate(date, teamId);

        insTaskDTO.setShiftType(shiftType);

        LocalDateTime d = DateUtils.string2LocalDateTime((String) params.get("date"), datePattern);

        // 查询日期对应的早晚班时间段
        InsTaskDate insTaskDate = insTaskDateService
                .getOne(new LambdaQueryWrapper<InsTaskDate>()
                        .eq(InsTaskDate::getDateTime, d).eq(InsTaskDate::getTaskId, taskId));

        // 添加巡检时间
        if (insTaskDate != null) {
            if (insTaskDate.getInspectMorningStartDate() != null)
                insTaskDTO.setInspectMorningStartDate(df.format(insTaskDate.getInspectMorningStartDate()));
            if (insTaskDate.getInspectMorningEndDate() != null)
                insTaskDTO.setInspectMorningEndDate(df.format(insTaskDate.getInspectMorningEndDate()));
            if (insTaskDate.getInspectNightStartDate() != null)
                insTaskDTO.setInspectNightStartDate(df.format(insTaskDate.getInspectNightStartDate()));
            if (insTaskDate.getInspectNightEndDate() != null)
                insTaskDTO.setInspectNightEndDate(df.format(insTaskDate.getInspectNightEndDate()));
            // 获取对应人员集合
            List<InsTaskUser> insTaskUserList = insTaskUserService
                    .list(new LambdaQueryWrapper<InsTaskUser>().eq(InsTaskUser::getTaskDateId, insTaskDate.getId()));

            // 取出人员id
            List<Integer> userIds = insTaskUserList.stream().map(InsTaskUser::getUserId).collect(Collectors.toList());

            insTaskDTO.setUserTeam(userIds);
        } else {
            throw new RRException(RRExceptionEnum.NOT_SCHEDULING);
        }


        return insTaskDTO;
    }

    @Override
    @Transactional
    public void schedulingUpdate(InsTaskVO insTaskVO) {

        InsTaskDTO insTaskDTO = InsTaskUtil.insTaskVO2InsTaskDTO(insTaskVO);

        List<Integer> users = insTaskVO.getUserTeam();

        Integer taskId = insTaskVO.getTaskId();

        LocalDateTime dateTime = DateUtils.string2LocalDateTime(insTaskVO.getDateTime(), datePattern);

        // 根据日期查询指定记录进行修改
        InsTaskDate insTaskDate = insTaskDateService
                .getOne(new LambdaQueryWrapper<InsTaskDate>()
                        .eq(InsTaskDate::getDateTime, dateTime).eq(InsTaskDate::getTaskId, insTaskDTO.getTaskId()));

        if (insTaskDate != null) {

            Integer taskDateId = insTaskDate.getId();
            BeanUtils.copyProperties(insTaskDTO, insTaskDate);

            insTaskDateService.updateById(insTaskDate);

            // 先删除执行人
            insTaskUserService.remove(new LambdaQueryWrapper<InsTaskUser>().eq(InsTaskUser::getTaskDateId, taskDateId));
            // 再重新添加
            // 排序
            int sort = 1;
            InsTaskUser insTaskUser;
            if (CollectionUtils.isNotEmpty(users)) {
                for (Integer user : users) {
                    insTaskUser = new InsTaskUser();
                    insTaskUser.setTaskId(taskId);
                    insTaskUser.setTaskDateId(taskDateId);
                    insTaskUser.setUserId(user);
                    insTaskUser.setIsUpdate(2);
                    insTaskUser.setSort(sort);
                    sort++;
                    insTaskUserService.save(insTaskUser);
                }
            }
        }

        // 修改任务子任务表
        InsTask insTask = baseMapper.selectById(taskId);
        if (insTask == null) {
            throw new RRException(RRExceptionEnum.TASK_NOT_EXIST);
        } else {
            // 删除已存在的数据
            insTaskStepService
                    .remove(new LambdaQueryWrapper<InsTaskStep>()
                            .eq(InsTaskStep::getTaskId, insTask.getId()).eq(InsTaskStep::getTaskStartDate, dateTime));

            // 设置最新的时间段
            insTask.setInspectMorningStartDate(insTaskDTO.getInspectMorningStartDate());
            insTask.setInspectMorningEndDate(insTaskDTO.getInspectMorningEndDate());
            insTask.setInspectNightStartDate(insTaskDTO.getInspectNightStartDate());
            insTask.setInspectNightEndDate(insTaskDTO.getInspectNightEndDate());

            List<LocalDateTime> localDateTimes = new ArrayList<>();
            localDateTimes.add(dateTime);

            // 保存子任务
            addTaskStep(localDateTimes, insTask, users);
        }

    }

    @Override
    public InsLineVO getAllPoint(Integer taskId, Integer stepId, SysUserEntity user) {

        String name = user.getName();

        Long userId = user.getUserId();

        InsTask insTask = getAllById(taskId);

        InsTaskStep insTaskStep = insTaskStepService.getById(stepId);

        if (insTask != null) {

            // 判断该任务有没有人点击过开始巡检
            if (insTaskStep.getExecutorId() == null && StringUtils.isEmpty(insTaskStep.getExecutor()) && insTaskStep.getExecStartDate() == null) {
                insTaskStep.setExecutor(name);
                insTaskStep.setExecutorId(userId);
                insTaskStep.setExecStartDate(LocalDateTime.now());
                insTaskStepService.updateById(insTaskStep);
                // 任务发布可以选择多个人,但是巡检的时候只能一人巡检,这个人点击之后其他人无法进行巡检操作,所以这里需要判断一下
            } else if (!insTaskStep.getExecutorId().equals(userId)) {
                throw new RRException(RRExceptionEnum.CURRENT_TASK_STARTED);
            }
            // 点击开始巡检即修改任务状态
            if (taskStepWhetherFinish(insTaskStep, insTask) >= 1) {
                insTask.setTaskStatus(TaskStatusEnum.START.getCode());
                baseMapper.updateDel(insTask);
            }
            Integer lineId = insTask.getLineId();
            return insLineService.detail(lineId, stepId);
        } else {
            throw new RRException(RRExceptionEnum.TASK_NOT_EXIST);
        }
    }


    @Override
    @Transactional
    public void commit(InspectionAppVO inspectionAppVO, SysUserEntity user) {

        /*
         * 1.修改子任务状态(ins_task_step)
         * 2. 1)查看子任务是否还有待完成任务,修改任务状态(ins_task)
         *    2)Y:说明下次还需要重复执行此任务,将任务状态修改为 1(未开始)
         *    3)N:说明此任务已经是最后一次,将任务状态修改为 3(已完成)
         * 3.新增巡检历史结果表,数据入库(ins_task_step_point)
         */
        Integer userId = user.getUserId().intValue();

        // 子任务id
        Integer stepId = inspectionAppVO.getTaskStepId();
        // 任务id
        Integer taskId = inspectionAppVO.getTaskId();

        InsTask insTask = getAllById(taskId);

        InsTaskStep insTaskStep = insTaskStepService.getById(stepId);

        InsPoint insPoint = insPointService.getAllById(inspectionAppVO.getPointId());

        String deviceName = insPoint.getDeviceName();

        if (insTask == null) {
            throw new RRException(RRExceptionEnum.CURRENT_TASK_NOT_EXIST);
        }
        if (insTaskStep == null) {
            throw new RRException(RRExceptionEnum.CURRENT_TASK_NOT_EXIST);
        }

        Integer shiftType = insTaskStep.getShiftType();
        Integer lineId = insTask.getLineId();
        InsLine insLine = insLineService.getAllById(lineId);

        // 发消息
        List<MsgInfo> msgInfoList = new ArrayList<>();

        // 发送给创建任务的人
        List<Integer> userIds = new ArrayList<>();
        // 添加组长
        SysUserEntity sysUserEntity = sysUserService.getTeamLeaderByUser(userId);
        if (sysUserEntity == null) {
            throw new RRException(RRExceptionEnum.NO_TEAM_LEADER);
        }
        userIds.add(sysUserEntity.getUserId().intValue());
        // 添加部门管理员
        Integer teamId = user.getTeamId();
        List<SysUserEntity> deptLeaderList;
        if (teamId != null) {
            deptLeaderList = sysUserService.getDeptLeaderByTeam(teamId);
        } else {
            Integer deptId = sysUserDeptService.getOne(new LambdaQueryWrapper<SysUserDeptEntity>().eq(SysUserDeptEntity::getUserId, userId)).getDeptId().intValue();
            deptLeaderList = sysUserService.getDeptLeaderByDept(deptId);
        }
        deptLeaderList.forEach(deptLeader -> userIds.add(deptLeader.getUserId().intValue()));

        if (taskStepWhetherFinish(insTaskStep, insTask) == 1) {
            // 1.修改子任务状态
            // 修改前需要判断是不是该任务上面所有的检点都已经完成
            insTaskStep.setStatus(InspectStepStatusEnum.FINISH.getCode());
            insTaskStep.setResultDate(LocalDateTime.now());
            insTaskStep.setUpdateUserId(userId);
            insTaskStepService.updateById(insTaskStep);

            // 查询该任务对应的子任务未完成条数
            int taskStepCount = insTaskStepService
                    .count(new LambdaQueryWrapper<InsTaskStep>()
                            .eq(InsTaskStep::getTaskId, taskId)
                            .eq(InsTaskStep::getStatus, InspectStepStatusEnum.CREATE.getCode()));

            // 2.修改任务状态
            if (taskStepCount > 0) {
                insTask.setTaskStatus(TaskStatusEnum.CREATE.getCode());
            } else {
                insTask.setTaskStatus(TaskStatusEnum.FINISH.getCode());
            }

            // 构造巡检完成消息对象
            String time = null;
            if (shiftType != null) {
                if (shiftType.equals(ShiftTypeEnum.DAY.getCode())) {
                    time = DateUtils.tf.format(insTaskStep.getMorningStartDate()) + "-" + DateUtils.tf.format(insTaskStep.getMorningEndDate());
                } else if (shiftType.equals(ShiftTypeEnum.NIGHT.getCode())) {
                    time = DateUtils.tf.format(insTaskStep.getNightStartDate()) + "-" + DateUtils.tf.format(insTaskStep.getNightEndDate());
                }
            } else {
                time = DateUtils.tf.format(insTaskStep.getMorningStartDate()) + "-" +
                        DateUtils.tf.format(insTaskStep.getMorningEndDate()) + "\t\t\t\t" +
                        DateUtils.tf.format(insTaskStep.getNightStartDate()) + "-" +
                        DateUtils.tf.format(insTaskStep.getNightEndDate());
            }

            InsMsgEntity insMsgEntity = new InsMsgEntity(insTaskStep.getTaskName(), insTask.getInspectType().toString(), insLine.getType().toString(), time);
            MsgInfo msgInfo = new MsgInfo("安全巡检", "安全巡检", "巡检完成", JSON.toJSONString(insMsgEntity));
            msgUserService.insertBatch(msgInfo, userIds);
        }
        insTask.setUpdateUserId(userId);
        baseMapper.updateDel(insTask);

        // 3.数据入库

        boolean overrunMsg = false;  // 是否存在超限
        // 检点检查内容集合
        List<InsPointSubitem> insPointSubs = inspectionAppVO.getInsPointSubitems();
        List<InsTaskStepPoint> insTaskStepPoints = new ArrayList<>();
        boolean signMsg = inspectionAppVO.getIsSign().equals(SignEnum.NO.getCode());    // 是否未签到
        for (InsPointSubitem e : insPointSubs) {
            InsTaskStepPoint insTaskStepPoint = new InsTaskStepPoint();
            BeanUtils.copyProperties(inspectionAppVO, insTaskStepPoint);
            insTaskStepPoint.setPointSubitemId(e.getId());
            insTaskStepPoint.setPhotoFile(JSON.toJSONString(e.getFileInfoModels()));
            insTaskStepPoint.setMemo(e.getMemo());
            // 1数值2状态
            Integer type = e.getType();
            if (type.equals(PointSubTypeEnum.NUM.getCode())) {

                // 最小值
                Integer min = e.getMinValue();
                // 最大值
                Integer max = e.getMaxValue();
                // 实时值
                Double realVal = Double.parseDouble(e.getRealVal());
                insTaskStepPoint.setValue(e.getRealVal());

                if ((min != null && realVal < min) || (max != null && realVal > max)) {
                    insTaskStepPoint.setStatusDescription("超出预设区间");
                    overrunMsg = true;
                }
                if (min != null && max != null && min <= realVal && max >= realVal) {
                    insTaskStepPoint.setStatusDescription("正常");
                }
                if ((min == null) && (max == null)) {
                    insTaskStepPoint.setStatusDescription("-");
                }

            } else if (type.equals(PointSubTypeEnum.STR.getCode())) {

                String val = e.getValue();
                insTaskStepPoint.setValue("-");
                // 状态时描述取value字段
                insTaskStepPoint.setStatusDescription(val);
            }
            insTaskStepPoints.add(insTaskStepPoint);
        }

        // 巡检完成本人不需要接收消息，报警需要接受消息
        userIds.add(userId);
        // 未签到消息对象
        if (signMsg) {
            InsMsgEntity insMsgEntity = new InsMsgEntity(insTaskStep.getTaskName(), insTaskStep.getExecutor(), deviceName);
            MsgInfo msgInfo = new MsgInfo("报警提示", "安全巡检", "现场巡检任务未签到", JSON.toJSONString(insMsgEntity));
            msgInfoList.add(msgInfo);
        }
        // 超限消息对象
        if (overrunMsg) {
            InsMsgEntity insMsgEntity = new InsMsgEntity(insTaskStep.getTaskName(), insTaskStep.getExecutor(), deviceName);
            MsgInfo msgInfo = new MsgInfo("报警提示", "安全巡检", "现场巡检任务超出设定值", JSON.toJSONString(insMsgEntity));
            msgInfoList.add(msgInfo);
        }
        // 添加消息
        taskExecutor.submit(() -> {
            msgUserService.insertBatch(msgInfoList, userIds);
        });
        insTaskStepPointService.saveBatch(insTaskStepPoints);
    }


    @Override
    public InsTask getAllById(Integer taskId) {
        return baseMapper.queryById(taskId);
    }

    @Override
    public List<InsTask> getAllByInspectType(Integer inspectType) {
        return baseMapper.queryByInspectType(inspectType);
    }

    /**
     * 判断当前子任务所有的检点是否已经全部检测完毕
     *
     * @param insTaskStep
     * @param insTask
     * @return
     */
    private int taskStepWhetherFinish(InsTaskStep insTaskStep, InsTask insTask) {

        Integer stepId = insTaskStep.getId();
        Integer taskId = insTask.getId();

        // 判断子任务和任务是否是父子关系
        if (!insTaskStep.getTaskId().equals(taskId)) {
            log.error("任务和子任务不对应 taskId={}, taskStep.taskId={}", taskId, insTaskStep.getTaskId());
//            throw new RRException("未知异常");
        }

        // 获取已完成的检点数
        Set<Integer> realPoints = insTaskStepService.getTaskStepPointChecked(stepId);
        // 查询该任务下共有几个检点
        Integer lineId = insTask.getLineId();
        List<InsPoint> needPoints = insLinePointService.getInsPointListByLineId(lineId);

        // 可能存在不需要巡检的点，减一下
        long count = needPoints.stream().filter(insPoint -> !insPoint.getRequireFlag()).count();
        if (CollectionUtils.isNotEmpty(needPoints)) {

            // 需完成检点数和已完成检点数一致
            return needPoints.size() - realPoints.size() - new Long(count).intValue();
        } else {
            log.error("线路中没有检点, 线路id={}", lineId);
        }
        return -999;
    }

    /**
     * 按天分组(最多跨一天)
     *
     * @param startDatetime 开始时间段
     * @param endDatetime   结束时间段
     * @param rate          频率(小时)
     */
    private List<List<LocalDateTime>> groupByDay(LocalDateTime startDatetime, LocalDateTime endDatetime, int rate) {

        // 结束日期 + 1天
        if (startDatetime.isAfter(endDatetime)) {
            endDatetime = endDatetime.plusDays(1);
        }

        List<List<LocalDateTime>> result = new ArrayList<>();

        // 频率小时转化为分钟
        long rateToMinute = rate * 60;

        // 每个任务时间段所需时间转化
        Duration duration = Duration.between(startDatetime, endDatetime);

        // 两个时间相差分钟
        long minutes = duration.toMinutes();

        List<LocalDateTime> localDateTimes;

        // 如果频率大于巡检时间段所需时间,算一次任务
        if (rateToMinute > minutes) {

            localDateTimes = Arrays.asList(startDatetime, endDatetime);
            result.add(localDateTimes);

        } else {

            // 总次数
            long time = minutes % rateToMinute == 0 ? minutes / rateToMinute : minutes / rateToMinute + 1;

            // 巡检开始时间 + 频率 = 下一次开始时间
            LocalDateTime firstDate = startDatetime.plusMinutes(rateToMinute);

            localDateTimes = Arrays.asList(startDatetime, firstDate);

            result.add(localDateTimes);

            for (int i = 1; i < time; i++) {

                if (i == time - 1) {
                    // 最后一个
                    localDateTimes = Arrays.asList(startDatetime.plusMinutes(rateToMinute * i), endDatetime);
                } else {
                    localDateTimes = Arrays.asList(startDatetime.plusMinutes(rateToMinute * i), startDatetime.plusMinutes(rateToMinute * (i + 1)));
                }
                result.add(localDateTimes);
            }
        }

        return result;
    }


    /**
     * 按周分组
     *
     * @param dateTimeList 所有日期
     */
    private List<List<LocalDateTime>> groupByWeek(List<LocalDateTime> dateTimeList) {

        // 开始是星期几
        int firstDate = dateTimeList.get(0).getDayOfWeek().getValue();

        List<List<LocalDateTime>> result;

        if (firstDate == 1) {
            result = ListUtils.fixedGrouping(dateTimeList, dayNum);
        } else {
            result = getWeek(dateTimeList, firstDate);
        }
        return result;
    }


    /**
     * 按月分组
     *
     * @param dateTimeList 所有日期
     */
    private List<List<LocalDateTime>> groupByMonth(List<LocalDateTime> dateTimeList) {

        List<List<LocalDateTime>> result = new ArrayList<>();

        List<LocalDateTime> temp = new ArrayList<>();

        for (LocalDateTime ld : dateTimeList) {
            // 首次添加
            if (temp.size() == 0) {
                temp.add(ld);
                // 如果只有一天,直接保存
                if (dateTimeList.size() == 1) {
                    result.add(temp);
                }
                continue;
            }
            // 月份相等添加
            if (temp.get(temp.size() - 1).getMonthValue() == ld.getMonthValue()) {
                temp.add(ld);
                // 最后一次添加
                if (ld.equals(dateTimeList.get(dateTimeList.size() - 1))) {
                    result.add(temp);
                }
            } else {
                // 不相等清空后重新添加
                result.add(temp);
                temp = new ArrayList<>();
                temp.add(ld);
            }
        }

        return result;
    }


    /**
     * 周分组子方法
     *
     * @param dateTimeList 开始到结束时间段内所有日期
     * @param startWeekDay 开始星期几
     */
    private List<List<LocalDateTime>> getWeek(List<LocalDateTime> dateTimeList, int startWeekDay) {

        List<List<LocalDateTime>> result = new ArrayList<>();

        List<LocalDateTime> firstWeek = new ArrayList<>();

        int length = dayNum - startWeekDay;
        if (length > dateTimeList.size()) {
            length = dateTimeList.size() - 1;
        }

        for (int i = 0; i <= length; i++) {
            firstWeek.add(dateTimeList.get(i));
        }

        result.add(firstWeek);

        // 添加完成后移除
        dateTimeList.removeAll(firstWeek);

        if (CollectionUtils.isNotEmpty(dateTimeList)) {
            List<List<LocalDateTime>> lists = ListUtils.fixedGrouping(dateTimeList, dayNum);
            result.addAll(lists);
        }

        return result;
    }

}
